#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_adc/adc_oneshot.h"

#include "pm.h"

#define TAG "PM"

static esp_timer_handle_t led_timer;             // LED定时器句柄
static esp_timer_handle_t batvol_detect_timer;   // 电池电压检测定时器句柄

/**
 * @brief 初始化ADC校准
 * @param unit ADC单元
 * @param channel ADC通道
 * @param atten 衰减设置
 * @param out_handle 输出校准句柄
 * @return true校准成功，false校准失败
 */
static bool adc_calibration_init(
    adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
    adc_cali_handle_t handle     = NULL;
    esp_err_t         ret        = ESP_FAIL;
    bool              calibrated = false;

#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
    if (!calibrated) {
        adc_cali_curve_fitting_config_t cali_config = {
            .unit_id  = unit,
            .chan     = channel,
            .atten    = atten,
            .bitwidth = ADC_BITWIDTH_DEFAULT,
        };
        ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
        if (ret == ESP_OK) {
            calibrated = true;
        }
    }
#endif

#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
    if (!calibrated) {
        adc_cali_line_fitting_config_t cali_config = {
            .unit_id  = unit,
            .atten    = atten,
            .bitwidth = ADC_BITWIDTH_DEFAULT,
        };
        ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
        if (ret == ESP_OK) {
            calibrated = true;
        }
    }
#endif

    *out_handle = handle;
    if (ret == ESP_OK) {
    } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
        ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
    } else {
        ESP_LOGE(TAG, "invalid arg or no memory");
    }

    return calibrated;
}

/**
 * @brief 释放ADC校准资源
 * @param handle 校准句柄
 */
static void adc_calibration_deinit(adc_cali_handle_t handle)
{
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
    ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));

#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
    ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
#endif
}

/**
 * @brief 检测电池电压
 * @param mv_out 输出电池电压值(mV)
 * @return ESP_OK成功，其他值失败
 */
esp_err_t check_battery_voltage(int *mv_out)
{
    esp_err_t  err         = ESP_OK;
    static int last_mv_out = 4200; // 默认电压值

    // 初始化ADC单元
    adc_oneshot_unit_handle_t   batvol_adc_handle;
    adc_oneshot_unit_init_cfg_t batvol_init_config = {.unit_id = BATVOL_ADC_UNIT};
    err                                            = adc_oneshot_new_unit(&batvol_init_config, &batvol_adc_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "failed to initialize battery voltage ADC");
        goto err;
    }

    // 配置ADC通道
    adc_oneshot_chan_cfg_t batvol_chan_config = {.atten = BATVOL_ADC_ATTEN, .bitwidth = ADC_BITWIDTH_DEFAULT};

    err = adc_oneshot_config_channel(batvol_adc_handle, BATVOL_ADC_CHANNEL, &batvol_chan_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "failed to config battery voltage channel");
        goto err;
    }

    // 初始化ADC校准
    bool              do_batvol_calibrated;
    adc_cali_handle_t batvol_cali_handle;
    do_batvol_calibrated =
        adc_calibration_init(BATVOL_ADC_UNIT, BATVOL_ADC_CHANNEL, BATVOL_ADC_ATTEN, &batvol_cali_handle);

    // 读取ADC原始值并转换为电压
    int batvol_adc_raw, batvol_adc_mv, batvol_mv;
    err = adc_oneshot_read(batvol_adc_handle, BATVOL_ADC_CHANNEL, &batvol_adc_raw);
    if (err == ESP_OK) {
        // ESP_LOGI(TAG, "battery voltage ADC raw value: %d", batvol_adc_raw);
        if (do_batvol_calibrated) {
            err = adc_cali_raw_to_voltage(batvol_cali_handle, batvol_adc_raw, &batvol_adc_mv);
            if (err == ESP_OK) {
                // ESP_LOGI(TAG, "battery voltage ADC value: %d mV", batvol_adc_mv);
            }
            // 根据分压电阻计算实际电池电压
            batvol_mv = batvol_adc_mv * (BATVOL_PULLUP_RES_VAL + BATVOL_PULLDOWN_RES_VAL) / BATVOL_PULLDOWN_RES_VAL;
            // ESP_LOGI(TAG, "battery voltage: %d mV", batvol_mv);
            last_mv_out = batvol_mv;
        }
    }

    // 清理资源
    adc_oneshot_del_unit(batvol_adc_handle);
    if (do_batvol_calibrated) {
        adc_calibration_deinit(batvol_cali_handle);
    }

err:
    *mv_out = last_mv_out;
    return err;
}

// ==================== LED指示灯控制 ====================

/**
 * @brief LED定时器回调函数，实现LED闪烁
 * @param arg 回调参数
 */
static void led_timer_callback(void *arg)
{
    gpio_set_level(LED_GPIO, !gpio_get_level(LED_GPIO));
}

/**
 * @brief 初始化电池电压LED指示灯
 * @return ESP_OK成功，其他值失败
 */
esp_err_t batvol_led_init(void)
{
    // 配置LED GPIO为输入输出模式
    gpio_config_t led_io_conf = {
        .pin_bit_mask = (1 << LED_GPIO),
        .mode         = GPIO_MODE_INPUT_OUTPUT,
        .pull_up_en   = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type    = GPIO_INTR_DISABLE,
    };
    gpio_config(&led_io_conf);

    // 创建LED闪烁定时器
    esp_timer_create_args_t timer_args = {
        .callback = &led_timer_callback, .arg = NULL, .dispatch_method = ESP_TIMER_ISR, .name = "led_timer"};
    ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_timer));

    return ESP_OK;
}

/**
 * @brief 设置LED工作模式
 * @param mode LED工作模式
 */
static void batvol_led_mode(led_mode_t mode)
{
    // 停止当前定时器
    if (esp_timer_is_active(led_timer)) {
        ESP_ERROR_CHECK(esp_timer_stop(led_timer));
    }

    switch (mode) {
    case LED_MODE_OFF:
        gpio_set_level(LED_GPIO, 0); // LED关闭
        break;
    case LED_MODE_ON:
        gpio_set_level(LED_GPIO, 1); // LED常亮
        break;
    case LED_MODE_BLINK:
        gpio_set_level(LED_GPIO, 1);                                  // 先点亮
        ESP_ERROR_CHECK(esp_timer_start_periodic(led_timer, 500000)); // 500ms周期闪烁
        break;
    case LED_MODE_BLINK_FAST:
        gpio_set_level(LED_GPIO, 1);                                  // 先点亮
        ESP_ERROR_CHECK(esp_timer_start_periodic(led_timer, 100000)); // 100ms周期快闪
        break;
    }
}

/**
 * @brief 电池电压检测定时器回调函数
 * @param arg 回调参数
 */
static void batvol_detect_timer_callback(void *arg)
{
    int mv_out;

    // 检测电池电压
    check_battery_voltage(&mv_out);
    ESP_LOGI(TAG, "battery voltage: %d mV", mv_out);

    // 根据电压值设置LED指示模式
    if (mv_out < 3300) {
        batvol_led_mode(LED_MODE_BLINK_FAST); // 电压严重不足，快闪警告
    } else if (mv_out < 3500) {
        batvol_led_mode(LED_MODE_BLINK); // 电压不足，慢闪提醒
    } else {
        batvol_led_mode(LED_MODE_OFF); // 电压正常，LED关闭
    }
}

esp_err_t batvol_monitor_init(void)
{
    // 创建电池电压检测定时器，每100秒检测一次
    esp_timer_create_args_t batvol_detect_timer_args = {
        .callback        = &batvol_detect_timer_callback,
        .arg             = NULL,
        .dispatch_method = ESP_TIMER_TASK,
        .name            = "batvol_detect_timer"};
    ESP_ERROR_CHECK(esp_timer_create(&batvol_detect_timer_args, &batvol_detect_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(batvol_detect_timer, 100 * 1000 * 1000)); // 100秒周期

    return ESP_OK;
}